using System;
using System.Collections.Generic;
using System.Text;
using LibGreen.Jabber.Connection;
using System.Net.Sockets;

namespace LibGreen.Jabber.Delegates
{
    /// <summary>
    /// A delegate to handle completed connection events
    /// </summary>
    /// <param name="sender">The <see cref="XmppConnection"/> that has completed its connection sequence</param>
    /// <param name="e">An <see cref="EventArgs"/> object</param>
    public delegate void ConnectionFinishedHandler(object sender, EventArgs e);

    /// <summary>
    /// A delegate to handle connection status update events
    /// </summary>
    /// <param name="sender">The <see cref="XmppConnection"/> that is updating</param>
    /// <param name="e">A <see cref="ConnectionUpdatedEventArgs"/> describing the event</param>
    public delegate void ConnectionUpdatedHandler(object sender, ConnectionUpdatedEventArgs e);

    /// <summary>
    /// Encapsulates information about a connection status update event
    /// </summary>
    public class ConnectionUpdatedEventArgs : EventArgs
    {
        private readonly ConnectionState oldState;
        private readonly ConnectionState newState;

        /// <summary>
        /// Initializes a new ConnectionUpdatedEventArgs
        /// </summary>
        public ConnectionUpdatedEventArgs(ConnectionState oldState, ConnectionState newState)
        {
            this.oldState = oldState;
            this.newState = newState;
        }

        /// <summary>
        /// The previous state of the connection
        /// </summary>
        public ConnectionState OldState
        {
            get { return oldState; }
        }

        /// <summary>
        /// The current state of the connection
        /// </summary>
        public ConnectionState NewState
        {
            get { return newState; }
        }
    }

    /// <summary>
    /// A delegate to handle SASL negotiation failure events
    /// </summary>
    /// <param name="sender">The <see cref="SASLNegotiator"/> that has failed</param>
    /// <param name="e">A <see cref="SaslNegotiationFailedEventArgs"/> describing the event</param>
    public delegate void SaslNegotiationFailedHandler(object sender, SaslNegotiationFailedEventArgs e);

    /// <summary>
    /// Describes the SASL negotiation failure
    /// </summary>
    public enum SaslFailureReason
    {
        /// <summary>
        /// Unknown failure
        /// </summary>
        Unknown,
        /// <summary>
        /// The server has received an &lt;abort&gt; from the client
        /// </summary>
        Aborted,
        /// <summary>
        /// The base-64 encoded data from an &lt;auth&gt; or &lt;response&gt; was badly formatted
        /// </summary>
        IncorrectEncoding,
        /// <summary>
        /// The client has sent an &lt;auth&gt; with an unrecognized "mechanism" attribute
        /// </summary>
        InvalidMechanism,
        /// <summary>
        /// The server requires a stronger authentication mechanism
        /// </summary>
        MechanismTooWeak,
        /// <summary>
        /// The client has passed bad credentials to the server
        /// </summary>
        BadCredentials,
        /// <summary>
        /// The server's authentication mechanism is temporarily out of service
        /// </summary>
        TemporaryAuthenticationFailure,
    }

    /// <summary>
    /// Encapsulates information about a SASL negotiation failure event
    /// </summary>
    public class SaslNegotiationFailedEventArgs : EventArgs
    {
        private readonly SaslFailureReason reason;

        /// <summary>
        /// Initializes a new SaslNegotiationFailedEventArgs
        /// </summary>
        /// <param name="reason">The reason for the SASL negotiation failure event</param>
        public SaslNegotiationFailedEventArgs(SaslFailureReason reason)
        {
            this.reason = reason;
        }

        /// <summary>
        /// Gets the reason for the SASL negotiation failure event
        /// </summary>
        public SaslFailureReason Reason
        {
            get { return reason; }
        }
    }

    /// <summary>
    /// A delegate to handle connection failure events
    /// </summary>
    /// <param name="sender">The <see cref="XmppConnection"/> that has failed</param>
    /// <param name="e">A <see cref="ConnectionFailedEventArgs"/> describing the event</param>
    public delegate void ConnectionFailedHandler(object sender, ConnectionFailedEventArgs e);

    /// <summary>
    /// Describes the reason for connection failure
    /// </summary>
    public enum ConnectionFailureReason
    {
        /// <summary>
        /// The connection failed for an unknown reason
        /// </summary>
        Unknown,
        /// <summary>
        /// The underlying socket was disconnected or disposed
        /// </summary>
        SocketFailure,
        /// <summary>
        /// TLS authentication failed
        /// </summary>
        TLSFailure,
        /// <summary>
        /// SASL authentication failed
        /// </summary>
        SASLFailure,
        /// <summary>
        /// The server reported a problem during resource binding
        /// </summary>
        BindingFailure,
        /// <summary>
        /// A session could not be established with the server
        /// </summary>
        SessionEstablishmentFailure,
    }

    /// <summary>
    /// Encapsulates information about a connection failure event
    /// </summary>
    public class ConnectionFailedEventArgs : EventArgs
    {
        private readonly ConnectionFailureReason reason;

        /// <summary>
        /// Initializes a new ConnectionFailedEventArgs
        /// </summary>
        /// <param name="reason">The reason for the connection failure event</param>
        public ConnectionFailedEventArgs(ConnectionFailureReason reason)
        {
            this.reason = reason;
        }

        /// <summary>
        /// Gets the reason for the connection failure event
        /// </summary>
        public ConnectionFailureReason Reason
        {
            get { return reason; }
        }
    }

    /// <summary>
    /// Provides a factory function for connecting sockets through a proxy
    /// </summary>
    /// <param name="host">The host to which to connect</param>
    /// <param name="port">The port on the host to which to connect</param>
    /// <param name="callback">A callback in which to receive the socket and, potentially, an error message.</param>
    /// <returns>A TCP stream socket ready to send and receive data</returns>
    /// <remarks>
    /// <para>This delegate is intended to be used if JabberLib is part of an application that provides a global
    /// socket connection factory through various proxies.</para>
    /// <para>The <paramref name="callback"/> parameter must be a delegate that accepts a Socket and a string as its parameters.</para>
    /// </remarks>
    public delegate void ProxiedSocketFactoryDelegate(string host, int port, Delegate callback);

    /// <summary>
    /// A delegate that satisfies the criteria for the <see cref="ProxiedSocketFactoryDelegate"/>
    /// </summary>
    /// <param name="socket">The <see cref="Socket"/> returned to the calling application</param>
    /// <param name="errormsg">If not IsNullOrEmpty, contains an error message describing the socket connection failure</param>
    internal delegate void ProxiedSocketFactoryResultHandler(Socket socket, string errormsg);
}
